#ifndef __GLESTexture_H__
#define __GLESTexture_H__


#include "U2GLESPreRequest.h"
#include "U2GLESSupport.h"
#include "U2Texture.h"
#include "U2HardwarePixelBuffer.h"


U2EG_NAMESPACE_BEGIN


class _U2GLESShare U2GLESTexture : public U2Texture
{
public:
    // Constructor
    U2GLESTexture(U2ResourceManager* creator, const U2String& name, U2ResourceHandle handle,
        const U2String& group, bool isManual, U2ManualResourceLoader* loader, 
        U2GLESSupport& support);

    virtual ~U2GLESTexture();

    void createRenderTexture();

    /// @copydoc U2Texture::getBuffer
    U2HardwarePixelBufferSharedPtr getBuffer(size_t face, size_t mipmap);

    /// Takes the U2 texture type (1d/2d/3d/cube) and returns the appropriate GL one
    GLenum getGLESTextureTarget(void) const;

    GLuint getGLID() const
    {
        return mTextureID;
    }
    
protected:
    /// @copydoc U2Texture::createInternalResourcesImpl
    void createInternalResourcesImpl(void);
    /// @copydoc Resource::prepareImpl
    void prepareImpl(void);
    /// @copydoc Resource::unprepareImpl
    void unprepareImpl(void);
    /// @copydoc Resource::loadImpl
    void loadImpl(void);
    /// @copydoc Resource::freeInternalResourcesImpl
    void freeInternalResourcesImpl(void);
    
    /** Internal method, create GLHardwarePixelBuffers for every face and
        mipmap level. This method must be called after the GL texture object was created,
        the number of mipmaps was set (GL_TEXTURE_MAX_LEVEL) and glTexImageXD was called to
        actually allocate the buffer
    */
    void _createSurfaceList();
    
    /// Used to hold images between calls to prepare and load.
    typedef U2SharedPtr<std::vector<U2Image> > LoadedImages;
    
    /** Vector of images that were pulled from disk by
        prepareLoad but have yet to be pushed into texture memory
        by loadImpl.  Images should be deleted by loadImpl and unprepareImpl.
    */
    LoadedImages mLoadedImages;

private:
    GLuint mTextureID;
    U2GLESSupport& mGLSupport;
    
    /// Vector of pointers to subsurfaces
    typedef std::vector<U2HardwarePixelBufferSharedPtr> SurfaceList;
    SurfaceList mSurfaceList;
};



/** Specialisation of U2SharedPtr to allow U2SharedPtr to be assigned to GLESTexturePtr
@note
    Has to be a subclass since we need operator=.
    We could templatise this instead of repeating per Resource subclass,
    except to do so requires a form VC6 does not support i.e.
    ResourceSubclassPtr<T> : public U2SharedPtr<T>
*/
class _U2GLESShare U2GLESTexturePtr : public U2SharedPtr<U2GLESTexture>
{
public:
    U2GLESTexturePtr() : U2SharedPtr<U2GLESTexture>() {}
    explicit U2GLESTexturePtr(U2GLESTexture* rep) : U2SharedPtr<U2GLESTexture>(rep) {}
    U2GLESTexturePtr(const U2GLESTexturePtr& r) : U2SharedPtr<U2GLESTexture>(r) {}

    U2GLESTexturePtr(const U2ResourcePtr& r) : U2SharedPtr<U2GLESTexture>()
    {
        // lock & copy other mutex pointer
        U2_MUTEX_CONDITIONAL(r.U2_AUTO_MUTEX_NAME)
        {
            U2_LOCK_MUTEX(*r.U2_AUTO_MUTEX_NAME)
            U2_COPY_AUTO_SHARED_MUTEX(r.U2_AUTO_MUTEX_NAME)
            pRep = static_cast<U2GLESTexture*>(r.getPointer());
            pUseCount = r.useCountPointer();
            if (pUseCount)
            {
                ++(*pUseCount);
            }
        }
    }

    U2GLESTexturePtr(const U2TexturePtr& r) : U2SharedPtr<U2GLESTexture>()
    {
        *this = r;
    }

    /// Operator used to convert a U2ResourcePtr to a GLESTexturePtr
    U2GLESTexturePtr& operator=(const U2ResourcePtr& r)
    {
        if (pRep == static_cast<U2GLESTexture*>(r.getPointer()))
        {
            return *this;
        }
        release();
        // lock & copy other mutex pointer
        U2_MUTEX_CONDITIONAL(r.U2_AUTO_MUTEX_NAME)
        {
            U2_LOCK_MUTEX(*r.U2_AUTO_MUTEX_NAME)
            U2_COPY_AUTO_SHARED_MUTEX(r.U2_AUTO_MUTEX_NAME)
            pRep = static_cast<U2GLESTexture*>(r.getPointer());
            pUseCount = r.useCountPointer();
            if (pUseCount)
            {
                ++(*pUseCount);
            }
        }
        else
        {
            // RHS must be a null pointer
            assert(r.isNull() && "RHS must be null if it has no mutex!");
            setNull();
        }
        return *this;
    }

    /// Operator used to convert a U2TexturePtr to a GLESTexturePtr
    U2GLESTexturePtr& operator=(const U2TexturePtr& r)
    {
        if (pRep == static_cast<U2GLESTexture*>(r.getPointer()))
            return *this;
        release();
        // lock & copy other mutex pointer
        U2_MUTEX_CONDITIONAL(r.U2_AUTO_MUTEX_NAME)
        {
            U2_LOCK_MUTEX(*r.U2_AUTO_MUTEX_NAME)
            U2_COPY_AUTO_SHARED_MUTEX(r.U2_AUTO_MUTEX_NAME)
            pRep = static_cast<U2GLESTexture*>(r.getPointer());
            pUseCount = r.useCountPointer();
            if (pUseCount)
            {
                ++(*pUseCount);
            }
        }
        else
        {
            // RHS must be a null pointer
            assert(r.isNull() && "RHS must be null if it has no mutex!");
            setNull();
        }
        return *this;
    }
};


U2EG_NAMESPACE_END


#endif
